package org.yu.team2023.flashdays;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.mojang.datafixers.util.Pair;
import lombok.Getter;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.ai.attributes.Attribute;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.item.ItemStack;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

public class TreasureData {
    @Getter
    protected final TreasureQuality quality;
    @Getter
    protected final MainTreasure mainTreasure;
    @Getter
    protected int level;

    @Getter
    protected final Map<AdditionTreasure, Pair<UUID, Double>> additionTreasures = new HashMap<>();
    @Getter
    final protected UUID mainUUID;

    @Getter
    protected int xp;

    public TreasureData(TreasureQuality quality,int slotNumber) {
        this.quality = quality;
        this.mainTreasure = TreasurePools.Main.role(slotNumber);
        this.mainUUID = UUID.randomUUID();
        this.level = 1;
        for (int i = 0; i < quality.getCount(); i++) {
            var rand = TreasurePools.Addition.role();
            int tryTimes = 0;
            while ((additionTreasures.containsKey(rand) || findSameAttribute(rand)) && tryTimes < TreasurePools.Addition.size()) {
                rand = TreasurePools.Addition.role();
                tryTimes++;
            }
            if (additionTreasures.containsKey(rand) && findSameAttribute(rand)) continue;
            this.additionTreasures.put(rand, Pair.of(UUID.randomUUID(), rand.getInitValue()));
        }
    }

    public int getTotalXpHas() {
        if (level == 1) return 5 + quality.ordinal() * 2;
        int total = 0;
        for (int i = 1; i < level; i++) {
            total += getXpNeeded(i);
        }
        return total + xp + 5 + quality.ordinal() * 2;
    }

    public Component getUpgradeProgress() {
        if (level != quality.getMaxLevel())
            return Component.translatable("tips.flashdays.upgrade_progress", xp, getXpNeeded(level));
        return Component.translatable("tips.flashdays.upgrade_progress_max");
    }

    public int getTotalXpNeed() {
        if (level == quality.getMaxLevel()) return 0;
        int total = 0;
        for (int i = level; i <= quality.getMaxLevel(); i++) {
            total += getXpNeeded(i);
        }
        return total;
    }

    private boolean findSameAttribute(AdditionTreasure additionTreasure) {
        return additionTreasure.getAttributeSupplier().get().equals(mainTreasure.getAttributeSupplier().get()) && additionTreasure.getOperation() == mainTreasure.getOperation();
    }

    public TreasureData(ItemStack stack) {
        var tag = stack.getOrCreateTagElement("TreasureSets");
        this.level = tag.getInt("Level");
        this.mainTreasure = TreasurePools.Main.getOrEmpty(tag.getString("Main"));
        this.quality = TreasureQuality.saveValueOf(tag.getString("Quality"));
        this.mainUUID = ModUtil.getUUIDSafe(tag, "MainUUID");
        this.xp = tag.getInt("xp");
        var list = tag.getList("Additions", Tag.TAG_COMPOUND);
        for (Tag tag1 : list) {
            CompoundTag c = (CompoundTag) tag1;
            AdditionTreasure additionTreasure = TreasurePools.Addition.getOrEmpty(c.getString("Id"));
            double v = c.getDouble("Value");
            UUID uuid = ModUtil.getUUIDSafe(c, "UUID");
            additionTreasures.put(additionTreasure, Pair.of(uuid, v));
        }
    }

    public static TreasureData safeCreate(ItemStack stack) {
        if (!stack.getOrCreateTag().contains("TreasureSets", Tag.TAG_COMPOUND)) return null;
        return new TreasureData(stack);
    }

    public void upgrade() {
        if (level >= quality.getMaxLevel()) return;
        level++;
        if (level >= quality.getMaxLevel()) xp = 0;
        if (level % 3 == 0) {
            if (additionTreasures.size() < quality.getCount()) {
                var rand = TreasurePools.Addition.role();
                this.additionTreasures.put(rand, Pair.of(UUID.randomUUID(), rand.getInitValue()));
            } else {
                var key = ModUtil.randomSelect(additionTreasures.keySet().stream().toList());
                additionTreasures.computeIfPresent(key, (additionTreasure, pair) -> Pair.of(pair.getFirst(), pair.getSecond() + additionTreasure.getUpgradeValue()));
            }
        }
    }

    public int upgrade(int xp) {
        if (level >= quality.getMaxLevel()) return xp;
        xp = xp + this.xp;
        for (int i = level; i < quality.getMaxLevel(); i++) {
            int xpNeeded = getXpNeeded(level);
            if (xpNeeded <= xp) {
                xp -= xpNeeded;
                upgrade();
            }
        }
        if (level >= quality.getMaxLevel())
            return xp;
        else {
            this.xp = xp;
            return 0;
        }
    }

    private int getXpNeeded(int level) {
        return (int) (quality.getStartXp() * Math.pow(quality.getXpMulti(), level - 1));
    }

    public Multimap<Attribute, AttributeModifier> getAttributeModifiers() {
        Multimap<Attribute, AttributeModifier> multimap = HashMultimap.create();
        multimap.put(mainTreasure.getAttributeSupplier().get(), new AttributeModifier(mainUUID, mainTreasure.getId(), mainTreasure.getStartAmount() + mainTreasure.getPerLevelAmount() * (level - 1), mainTreasure.getOperation()));
        additionTreasures.forEach((additionTreasure, uuidDoublePair) -> multimap.put(additionTreasure.getAttributeSupplier().get(), new AttributeModifier(uuidDoublePair.getFirst(), additionTreasure.getId(), uuidDoublePair.getSecond(), additionTreasure.getOperation())));
        return multimap;
    }

    public void save(ItemStack stack) {
        var tag = stack.getOrCreateTagElement("TreasureSets");
        tag.putInt("Level", level);
        tag.putString("Main", mainTreasure.getId());
        tag.putString("Quality", quality.name());
        tag.putUUID("MainUUID", mainUUID);
        tag.putInt("xp", xp);
        ListTag listTag = new ListTag();
        additionTreasures.forEach((additionTreasure, pair) -> {
            CompoundTag inner = new CompoundTag();
            inner.putString("Id", additionTreasure.getId());
            inner.putDouble("Value", pair.getSecond());
            inner.putUUID("UUID", pair.getFirst());
            listTag.add(inner);
        });
        tag.put("Additions", listTag);
    }

    public List<Component> getAttributesTooltip(List<Component> tooltips) {
        tooltips.add(Component.translatable("tip.treasure.quality."+quality.name()).withStyle(quality.getRarity().getStyleModifier()));
        tooltips.add(Component.translatable("tip.flashdays.treasureMain"));
        ModUtil.addModifiersTips(tooltips, mainTreasure.getAttributeSupplier().get(), new AttributeModifier(mainUUID, mainTreasure.getId(), mainTreasure.getStartAmount() + mainTreasure.getPerLevelAmount() * (level - 1), mainTreasure.getOperation()));
        tooltips.add(Component.translatable("tip.flashdays.treasureAdditions"));
        additionTreasures.forEach((additionTreasure, uuidDoublePair) -> ModUtil.addModifiersTips(tooltips, additionTreasure.getAttributeSupplier().get(), new AttributeModifier(uuidDoublePair.getFirst(), additionTreasure.getId(), uuidDoublePair.getSecond(), additionTreasure.getOperation())));
        return tooltips;
    }
}
